package simulator.meta.environment;

import java.awt.Color;
import java.util.LinkedList;

import javax.vecmath.Color3f;
import javax.vecmath.Vector3d;

import simbad.sim.Arch;
import simbad.sim.EnvironmentDescription;
import simbad.sim.SimpleAgent;
import simbad.sim.Wall;
import simulator.config.Config;
import simulator.meta.agent.AgentFactory;
import simulator.meta.environment.bean.EnviromentBean;
import util.essential.SourceGetter;
import util.xmlmanager.elements.Attribute;
import util.xmlmanager.elements.Element;

public class EnvironmentBuilderXML {
	//Constants:
	public final static String XML_TARGET_NAME = "environment";
	private final static String BASE_PATH = "config";
	private final static String WALL = "wall";
	private final static String ARCH = "arch";
	private final static String ROBOT = "robot";
	private final static String RADIUS = "radius";
	private final static String ENVIRONMENT = "environment";
	private final static String POSITION = "position";
	private final static String LENGTH = "length";
	private final static String HEIGHT = "height";
	private final static String ROTATED = "rotated";
	private final static String COLOR = "color";
	
	private final static String PROMPT = "EnviromentBuilder> ";
	public final static String SPLITTER = ";";
	public final static String SECONDARY_SPLITTER = ",";
	
	/**
	 ******* IMPORTANT!!! Please, for environment description use "config.dtd".
	 * 
	 * XML File Structure:
	 * WALL: position(x,y,z), length, height, isRotated (90º), [color(R,G,B)]
	 * ROBOT: position(x,y,z), name
	 * TYPE: number
	 */
	/*Example:
	//<?xml version='1.0' encoding='utf-8'?>
	//<!-- <!DOCTYPE config SYSTEM "config.dtd"> -->
	//<!--  Environment File Descriptor  -->
	//<config target="environment">
	//	<wall>
	//	 	<position x="1" y="2" z="1"/>
	//	 	<length value="8"/>
	//	 	<height value="1"/>
	//	 	<rotated status="true"/>
	//	 	<color red="0" green="255" blue="0"/>
	//	</wall>
	//  <arch>
	//	 	<position x="1" y="2" z="1"/>
	//	 	<rotated status="true"/>
	//	</arch>
	//	<robot name="R1">
	//	 	<position x="1" y="2" z="1"/>
	//	</robot>
	//</config>
	 * */
	//Methods:
	public static EnviromentBean parse(String xmlScriptFileName,EnvironmentDescription environmentDescription) {
		EnviromentBean enviromentBean = new EnviromentBean();
		SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT, "Parsing script file name: \""+xmlScriptFileName+"\"...");
		SourceGetter.xmlManager.addXMLDocument(xmlScriptFileName);
		
		//Get environment configuration:
		//Making constraint:
		LinkedList<Attribute> constraint = new LinkedList<Attribute>();
		constraint.add(new Attribute("target",EnvironmentBuilderXML.XML_TARGET_NAME));
		LinkedList<Element>elements = SourceGetter.xmlManager.select(EnvironmentBuilderXML.BASE_PATH, constraint);
		
		LinkedList<Element>subelements;
		for (Element element : elements) {
			//Getting walls description list:
			subelements = element.getElements();
			
			for (Element subelement : subelements) {
				if(subelement.getName().equals(EnvironmentBuilderXML.WALL)){
					SourceGetter.out.debug("=================WALLS:");
					Wall tmp = getWall(subelement,environmentDescription);
					enviromentBean.addWall(tmp);
					SourceGetter.out.debug(tmp.toString());
					SourceGetter.out.debug("=================");
				}else if(subelement.getName().equals(EnvironmentBuilderXML.ARCH)){
					SourceGetter.out.debug("=================ARCH:");
					Arch tmp = EnvironmentBuilderXML.getArch(subelement,environmentDescription);
					enviromentBean.addArch(tmp);
					SourceGetter.out.debug(tmp.toString());
					SourceGetter.out.debug("=================");
				}else if(subelement.getName().equals(EnvironmentBuilderXML.ROBOT)){
					SourceGetter.out.debug("=================ROBOTS:");
					SimpleAgent tmp = EnvironmentBuilderXML.getAgent(subelement);
					enviromentBean.addAgent(tmp);
					SourceGetter.out.debug(tmp.toString());
					SourceGetter.out.debug("=================");
				}else if(subelement.getName().equals(EnvironmentBuilderXML.ENVIRONMENT)){
					SourceGetter.out.debug("=================ENVIRONMENT:");
					Attribute attribute = subelement.getAttribute("size");
					SourceGetter.out.debug(attribute.toString());
					enviromentBean.setEnvironmentSize(Float.parseFloat(attribute.getValue()));
					attribute = subelement.getAttribute("numberofsidetiles");
					SourceGetter.out.debug(attribute.toString());
					enviromentBean.setNumberOfSideTiles(Integer.parseInt(attribute.getValue()));
					SourceGetter.out.debug("=================");
				}else{
					SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[parse] ", "Wrong definition \""+subelement.getName()+"\"!!!");
				}
			}
		}
		return enviromentBean;
	}

	// ROBOT: position(x,y,z), name
	//--------------Example:--------------
	//	<robot name="R1">
	//	 	<position x="1" y="2" z="1"/>
	//	</robot>
	/**
	 * @param agentDescription
	 * @param environmentDescription
	 * @return agent.
	 */
	private static SimpleAgent getAgent(Element agentDescription){
		//Getting values:
		Vector3d position = EnvironmentBuilderXML.getPosition(agentDescription, EnvironmentBuilderXML.POSITION);
		Color color = EnvironmentBuilderXML.getColor(agentDescription, EnvironmentBuilderXML.COLOR);
		float radius = EnvironmentBuilderXML.getFloatValue(agentDescription, EnvironmentBuilderXML.RADIUS);
		Attribute attribute = agentDescription.getAttribute("name");
		String name = "Unknown Agent";
		if(attribute != null){
			name = attribute.getValue();
		}
		attribute = agentDescription.getAttribute("type");
		String type = "normal";
		if(attribute != null){
			type = attribute.getValue();
		}
		SourceGetter.out.debug("%%%%%%% Type of robot: "+type);
		//Building Agent:
		SimpleAgent agent = AgentFactory.getAgent(type,radius,position,name);
		agent.setColor(new Color3f(color));
		return agent;
	}

	//WALL: position(x,y,z), length, height, isRotated (90º), [color(R,G,B)]
	//--------------Example:--------------
	//	<wall>
	//	 	<position x="1" y="2" z="1"/>
	//	 	<length value="8"/>
	//	 	<height value="1"/>
	//	 	<rotated status="true"/>
	//	 	<color red="0" green="255" blue="0"/>
	//	</wall>
	/**
	 * @param wallDescription
	 * @param environmentDescription
	 * @return Wall.
	 */
	private static Wall getWall(Element wallDescription, EnvironmentDescription environmentDescription){
		//Getting values:
		Vector3d position = EnvironmentBuilderXML.getPosition(wallDescription, EnvironmentBuilderXML.POSITION);
		int length = EnvironmentBuilderXML.getIntValue(wallDescription, EnvironmentBuilderXML.LENGTH);
		int height = EnvironmentBuilderXML.getIntValue(wallDescription, EnvironmentBuilderXML.HEIGHT);
		boolean rotation = EnvironmentBuilderXML.getRotation(wallDescription, EnvironmentBuilderXML.ROTATED);
		Color color = EnvironmentBuilderXML.getColor(wallDescription, EnvironmentBuilderXML.COLOR);
		//Building wall:
		Wall wall = new Wall(position, length, height, environmentDescription);
		wall.rotate90((rotation)?1:0);
		wall.setColor(new Color3f(color));
		
		return wall;
	}
	//ARCH: position(x,y,z), isRotated (90º), [color(R,G,B)]
	//--------------Example:--------------
	//	<arch>
	//	 	<position x="1" y="2" z="1"/>
	//	 	<rotated status="true"/>
	//	</arch>
	/**
	 * @param wallDescription
	 * @param environmentDescription
	 * @return Wall.
	 */
	private static Arch getArch(Element archDescription, EnvironmentDescription environmentDescription){
		//Getting values:
		Vector3d position = EnvironmentBuilderXML.getPosition(archDescription, EnvironmentBuilderXML.POSITION);
		boolean rotation = EnvironmentBuilderXML.getRotation(archDescription, EnvironmentBuilderXML.ROTATED);
		//Building wall:
		Arch arch = new Arch(position, environmentDescription);
		arch.rotate90((rotation)?1:0);
		return arch;
	}
	/**
	 * @param baseElement
	 * @param target
	 * @return element of "baseElement" with name "target". In case that exist either more than one or none, returns null.
	 */
	private static Element getDescriptorElement(Element baseElement, String target){
		LinkedList<String> path;
		path = new LinkedList<String>();
		path.add(baseElement.getName());
		path.add(target);
		LinkedList<Element> tmp = baseElement.find(path);
		if(tmp!=null && !tmp.isEmpty() && tmp.size()==1){
			return tmp.getFirst();
		}else{
			return null;
		}
	}
	/**
	 * @param baseElement
	 * @param target
	 * @return 3D position (x,y,z). By default returns (0,0,0).
	 */
	private static Vector3d getPosition(Element baseElement, String target){
		Vector3d ans = new Vector3d(0d,0d,0d);// x,y,z.
		//Getting position:
		Element element = EnvironmentBuilderXML.getDescriptorElement(baseElement, target);
		if(element!=null){
			Attribute attr;
			attr = element.getAttribute("x");
			ans.setX((attr!=null)?Double.parseDouble(attr.getValue()):ans.getX());
			attr = element.getAttribute("y");
			ans.setY((attr!=null)?Double.parseDouble(attr.getValue()):ans.getY());
			attr = element.getAttribute("z");
			ans.setZ((attr!=null)?Double.parseDouble(attr.getValue()):ans.getZ());
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getPosition] ", "Position description: <x,y,z>"+ans.toString()+".");
		}else{
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getPosition] ", "Wrong position description!!! Returning default position: <x,y,z>"+ans.toString()+".");
		}
		return ans;
	}
	
	/**
	 * @param baseElement
	 * @param target
	 * @return length value. By default it is 1.
	 */
	private static int getIntValue(Element baseElement, String target){
		int ans = 1;
		//Getting position:
		Element element = EnvironmentBuilderXML.getDescriptorElement(baseElement, target);
		if(element!=null){
			Attribute attr;
			attr = element.getAttribute("value");
			ans = ((attr!=null)?Integer.parseInt(attr.getValue()):ans);
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getIntValue] ", "\""+target+"\" description: <value>("+ans+").");
		}else{
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getIntValue] ", "Wrong \""+target+"\" description!!! Returning default \""+target+"\": <value>("+ans+").");
		}
		return ans;
	}
	
	/**
	 * @param baseElement
	 * @param target
	 * @return length value. By default it is 1.
	 */
	private static float getFloatValue(Element baseElement, String target){
		float ans = 1;
		//Getting position:
		Element element = EnvironmentBuilderXML.getDescriptorElement(baseElement, target);
		if(element!=null){
			Attribute attr;
			attr = element.getAttribute("value");
			ans = ((attr!=null)?Float.parseFloat(attr.getValue()):ans);
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getFloatValue] ", "\""+target+"\" description: <value>("+ans+").");
		}else{
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getFloatValue] ", "Wrong \""+target+"\" description!!! Returning default \""+target+"\": <value>("+ans+").");
		}
		return ans;
	}
	
	/**
	 * @param baseElement
	 * @param target
	 * @return rotation. By default it is "false".
	 */
	private static boolean getRotation(Element baseElement, String target){
		boolean ans = false;
		//Getting position:
		Element element = EnvironmentBuilderXML.getDescriptorElement(baseElement, target);
		if(element!=null){
			Attribute attr;
			attr = element.getAttribute("status");
			ans = ((attr!=null)?Boolean.parseBoolean(attr.getValue()):ans);
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getRotation] ", "Rotation description: <rotation>("+ans+").");
		}else{
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getRotation] ", "Wrong rotation description!!! Returning default rotation: <rotation>("+ans+").");
		}
		return ans;
	}
	
	/**
	 * @param baseElement
	 * @param target
	 * @return color. By default it is BLACK.
	 */
	private static Color getColor(Element baseElement, String target){
		Color ans = Color.BLACK;
		//Getting position:
		Element element = EnvironmentBuilderXML.getDescriptorElement(baseElement, target);
		if(element!=null){
			Attribute red = element.getAttribute("red");
			Attribute green = element.getAttribute("green");
			Attribute blue = element.getAttribute("blue");
			ans = new Color(((red!=null)?Integer.parseInt(red.getValue()):ans.getRed()),
					((green!=null)?Integer.parseInt(green.getValue()):ans.getGreen()),
					((blue!=null)?Integer.parseInt(blue.getValue()):ans.getBlue()));
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getColor] ", "Color description: <r,g,b>"+ans.toString()+".");
		}else{
			SourceGetter.out.debug(EnvironmentBuilderXML.PROMPT+"[getColor] ", "Wrong color description!!! Returning default color: <r,g,b>("+ans.toString()+").");
		}
		return ans;
	}
	
	//Just for test:
	public static void main(String[] args) {
		EnvironmentBuilderXML.parse(Config.SIMULATION_DESCRIPTOR, null);
	}
}
